package neca.tmazo.modelo;

import java.util.Stack;

import javax.swing.UIManager;

import neca.tmazo.datasets.BBDDException;
import neca.tmazo.datasets.Music;
import neca.tmazo.datasets.User;
import neca.tmazo.persistencia.PersistencyFacade;
import neca.tmazo.persistencia.PersistencyFacadeBBDDMySQL;
import neca.tmazo.vista.VistaFacade;
import neca.tmazo.vista.vistaGUI.VistaFacadeGUI;

/**
 * 
 * @author neca
 *
 * Clase principal de la aplicacion. Se encarga de gestionar la ventana modelo actual, de inicializar y almacenar las facades, etc.
 *
 */
public class Aplicacion {
	
	private VistaFacade vistaFacade; // Fachada vista.
	private PersistencyFacade persistencyFacade;// Fachada persistencia.

	private VentanaModelo lastWindow = null; // Ultima ventana en ejecucion.
	private VentanaModelo currentWindow = null; // Ventana actual en ejecucion
	private boolean changeWindow = false; // Flag que indica si se ha solicitado un cambio de ventana

	private User user; // Usuario que va a iniciar sesion
	private Music[] userMusic;
	
	private Stack<VentanaModelo> pila; // Pila de ventanas para desarrollar el boton atras
	private boolean reloadLibrary=true;
	private boolean reloadMainMenu=true;
	
	public Aplicacion() {
		// Inicializamos la pila
		pila = new Stack<VentanaModelo>();
		
		// Iniciamos la fachada vista con la implementacion de GUI
		this.vistaFacade = new VistaFacadeGUI();
		
		// Iniciamos la fachada persistencia con la implementacion en MYSQL
		VentanaModelo firstScreen;
		try {
			this.persistencyFacade = new PersistencyFacadeBBDDMySQL();
			// La primera pantalla ser� el login
			firstScreen = new VentanaModeloLogin(this);
			
		} catch (BBDDException e) {
			//Mostramos una primera ventana de error fatal
			firstScreen = new VentanaModeloErrorFatal(this,"Error al conectar con la BBDD");
		}

		setCurrentWindow(firstScreen);
		
		// Iniciamos el bucle infinito de ejecucion
		init();
	}
	
	/**
	 * Abre ventanas y se encarga de cerrarlas y abrir nuevas cuando se solicite
	 */
	public void init(){
		while(true){
			this.changeWindow=false;
			this.currentWindow.init(); //Iniciamos la ventana actual
			synchronized(this){
				//Si cuando lleguemos aqui aun no se ha solicitado un cambio de ventana, nos bloqueamos hasta que se nos notifique.
				while(!this.changeWindow){
					try {
						wait();
					} catch (InterruptedException e) {}
				}
			}
			//Se ha solicitado un cambio de ventana. Cerramos con seguridad la ultima ventana en ejecuci�n
			this.lastWindow.close();
			this.lastWindow=null;
		}
	}
	
	/**
	 * Devuelve la fachada vista
	 */
	public VistaFacade getVistaFacade() {
		return this.vistaFacade;
	}

	/**
	 * Devuelve la fachada persistencia
	 */
	public PersistencyFacade getPersistencyFacade() {
		return this.persistencyFacade;
	}

	/**
	 * Actualiza la ventana modelo activa
	 */
	public synchronized void setCurrentWindow(VentanaModelo newWindow) {
		//Notificamos a vista que se va a hacer un cambio de ventana, para que muestre una pantalla de cargando (o similar)
		this.vistaFacade.notifyWindowChange();
		//Cambiamos la ventana actual y notificamos al hilo de modelo que puede cambiar, en caso de que se encuentre ya bloqueado.
		this.lastWindow = this.currentWindow;
		this.currentWindow = newWindow;
		this.changeWindow = true;
		notifyAll();
	}
	/**
	 * Devuelve el usuario actual
	 */
	public User getUser() {
		return user;
	}
	/**
	 * Actualiza el usuario actual
	 */
	public void setUser(User newUser) {
		this.user = newUser;
	}

	public Stack<VentanaModelo> getPila() {
		return pila;
	}

	public void setPila(Stack<VentanaModelo> pila) {
		this.pila = pila;
	}
	
	/**
	 * Desloguea (Hace el usuario igual a nulo, vacia la pila, vuelve a la pantalla principal...)
	 */
	public void logout()
	{
		this.getPila().removeAllElements();
		this.setUser(null);
		this.setUserMusic(null);
		setReloadLibrary(true);
		setReloadMainMenu(true);
		setCurrentWindow(new VentanaModeloLogin(this));
	}

	/**
	 * Cierra la aplicacion. A incluir en este metodo todo el protocolo que se
	 * ha de seguir antes de cerrar (Desconexion con la BD, guardado de datos
	 * criticos, etc.)
	 */
	public void onExit() {
		this.getPila().removeAllElements();
		this.setUser(null);
		this.setUserMusic(null);
		System.out.println("Cerrando");
		System.exit(0);
	}
	/**
	 * Metodos que guardan si el estado de ciertos datos guardados en la aplicacion
	 * es consistente para evitar hacer consultas en la BD de forma constante
	 */
	public void setReloadLibrary(boolean rl){this.reloadLibrary=rl;}
	public void setReloadMainMenu(boolean rl){this.reloadMainMenu=rl;}
	public boolean getReloadLibrary()
	{
		return this.reloadLibrary;
	}
	public boolean getReloadMainMenu()
	{
		return this.reloadMainMenu;
	}
	/**
	 * Si hemos dejado almacenada la musica del usuario (La musica de Mi Recopilatorio o de Hoy Recomendamos) la devolvemos.
	 * Usamos esto para evitar hacer consultas innecesarias a la BD.
	 */
	public Music[] getUserMusic()
	{
		return this.userMusic;
	}
	public void setUserMusic(Music[] m)
	{
		this.userMusic=m;
	}
	// ***************************************** MAIN ************************************************//
	public static void main(String[] args) {
		
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e){
			System.err.println("No se ha podido establecer el 'Look And Feel' del sistema, se establece el de Java");
		}
		
		new Aplicacion();
	}
}
